use prost_types::field_descriptor_proto::Type;
use thiserror::Error;

#[derive(Error, Debug)]
pub enum ExtendError {
    #[error("Type group should be deprecated")]
    GroupTypeDeprecated,
    #[error("Explicit type name required when type is message or enum")]
    ExplicitTypeRequired,
}

pub(crate) trait AsStrType {
    fn as_str_type<'a>(&self, explicit_type: Option<&'a String>) -> Result<&'a str, ExtendError>;
	#[allow(unused)]
    fn as_string_type(&self, package: Option<&String>, explicit_type: Option<String>) -> Result<String, ExtendError>;
}

impl AsStrType for Type {
    fn as_str_type<'a>(&self, explicit_type: Option<&'a String>) -> Result<&'a str, ExtendError> {
        match self {
            Type::Double => Ok("double"),
            Type::Float => Ok("float"),
            Type::Int64 => Ok("int64"),
            Type::Uint64 => Ok("uint64"),
            Type::Int32 => Ok("int32"),
            Type::Fixed64 => Ok("fixed64"),
            Type::Fixed32 => Ok("fixed32"),
            Type::Bool => Ok("bool"),
            Type::String => Ok("string"),
            Type::Group => Err(ExtendError::GroupTypeDeprecated),
            Type::Message | Type::Enum => Ok(explicit_type.unwrap().as_str()),
            Type::Bytes => Ok("bytes"),
            Type::Uint32 => Ok("uint32"),
            Type::Sfixed32 => Ok("sfixed64"),
            Type::Sfixed64 => Ok("sfixed64"),
            Type::Sint32 => Ok("sint32"),
            Type::Sint64 => Ok("sint32"),
        }
    }

    fn as_string_type(&self, package: Option<&String>, explicit_type: Option<String>) -> Result<String, ExtendError> {
        match self {
            Type::Group => Err(ExtendError::GroupTypeDeprecated),
            Type::Message | Type::Enum => {
                match explicit_type {
                    None => Err(ExtendError::ExplicitTypeRequired),
                    Some(explicit_type) => {
                        let new_type = explicit_type.clone();
                        let mut new_type_str = new_type.as_str();
                        new_type_str = explicit_type.strip_prefix(".").unwrap_or(new_type_str);
                        if let Some(package) = package {
                            new_type_str = new_type_str.strip_prefix(package).unwrap_or(new_type_str);
                            new_type_str = new_type_str.strip_prefix(".").unwrap_or(new_type_str);
                        }
                        Ok(new_type_str.to_string())
                    }
                }
            }
            _ => Ok(self.as_str_type(None)?.to_string()),
        }
    }
}